home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Fatted Calf
/
The Fatted Calf.iso
/
Applications
/
Utilities
/
Stuart
/
slog
/
slog.m
< prev
next >
Wrap
Text File
|
1992-08-10
|
9KB
|
359 lines
/* Replacement for StuartLog in Stuart2.4.
*
* This program is in the public domain. Use and abuse it as you see fit.
*
* This program requires setuid-root to run correctly. There's
* now a Makefile that sets all of that up - so use make to compile
* this. There will be warnings under NeXSTEP3.0. Of course,
* consult Stuart's online documentation under Installation/slot
* for more information.
*
*
* The StuartLog tool in Stuart2.3 was a good idea, but it didn't
* quite work. I found that it would often simply hang during the
* login process, thus either hanging Stuart or not getting the
* logging functions done. slog takes a new approach. It runs
* continuously from the time Stuart is launched, and the same process
* handles all logging functions. Stuart communicates with slog
* via a private Speaker/Listener pair. slog also monitors the
* parent Stuart process and exits on abnormal termination.
*
* Admire the code to connect to the parent process. I think it
* cost me a kidney.
*
* scott hess
* shess@ssesco.com
*/
#import "SLogListener.h"
#import <objc/HashTable.h>
#import <libc.h>
#import <grp.h>
#import <lastlog.h>
#import <utmp.h>
#import <ttyent.h>
#import <pwd.h>
#import <mach.h>
#import <mach_error.h>
#import <dpsclient/dpsclient.h>
#import <sys/notify.h>
@interface SLogger : Object
{
SLogListener *listener;
HashTable *slots;
int uid;
const char *name;
port_t parentNotify;
}
- run;
@end
/* Locking open and close. Though flock() is not a good general-purpose
* file locker due to NFS limitations. It works well for this case
* since only the local machine can access the devices.
*/
int lopen( const char *filename, int openFlags)
{
int fd=open( filename, openFlags);
flock( fd, LOCK_EX);
return fd;
}
int lclose( int fd)
{
flock( fd, LOCK_UN);
return close( fd);
}
/* Fix ownerships and permissions on the named pty line. */
void fixOwnership( const char *pty, int uid, int gid, int mod)
{
char dev[ 64];
sprintf( dev, "/dev/%s", pty);
chown( dev, uid, gid);
chmod( dev, mod);
}
/* Write an entry to wtmp. */
void writeWtmp( struct utmp *ut)
{
int f=lopen( "/usr/adm/wtmp", O_WRONLY | O_APPEND);
if( f>=0) {
write( f, ut, sizeof( struct utmp));
lclose( f);
} else {
perror( "opening /usr/adm/wtmp");
}
}
/* Write an entry to utmp. */
void writeUtmp( struct utmp *ut, int slot)
{
if( slot>-1) {
int f=lopen( "/etc/utmp", O_WRONLY);
if( f>=0) {
lseek( f, slot*sizeof( struct utmp), L_SET);
write( f, ut, sizeof( struct utmp));
lclose( f);
} else {
perror( "opening /etc/utmp");
}
}
}
@implementation SLogger
/* Initialize uid to an invalid user id (0 is valid). */
- init
{
self=[super init];
if( self) {
uid=-1;
}
return self;
}
/* Find the slot in the /etc/ttys file for the given device. Cache
* a mapping from the device name to the slot number for future use.
*/
-(int)getSlot:(const char *)device
{
if( ![slots isKey:device]) {
struct ttyent *t;
int slot;
setttyent();
for( slot=1; t=getttyent(); slot++) {
if( !strcmp( device, t->ty_name)) {
break;
}
}
endttyent();
if( !t) {
slot=-1;
}
if( !slots) {
slots=[HashTable allocFromZone:[self zone]];
slots=[slots initKeyDesc:"*" valueDesc:"i" capacity:0];
}
device=NXUniqueString( device);
[slots insertKey:device value:(void *)slot];
return slot;
} else {
return (int)[slots valueForKey:device];
}
}
/* Login a use on the given pty. */
-(int)login:(char *)pty ownerships:(int)ownership
utmp:(int)utmp wtmp:(int)wtmp lastlog:(int)lastlog
{
/* Cache a passwd entry for the user if needed. */
if( uid==-1) {
/* Grab a passwd entry, set up uid. This code was suggested
* by der Mouse <mouse@larry.mcrcim.mcgill.edu>
*/
char *user=getenv( "USER");
struct passwd *pw=NULL;
uid=getuid();
if( user) {
pw=getpwnam( user);
}
if( !pw || (uid && (uid!=pw->pw_uid))) {
pw=getpwuid( uid);
}
if( pw) {
uid=pw->pw_uid;
}
if( pw) {
name=NXUniqueString( pw->pw_name);
} else {
name="Unknown";
}
}
if( utmp || wtmp || lastlog) {
struct utmp ut;
/* Clean up the utmp entry. */
bzero( &ut, sizeof( ut));
/* Set up the ut_name field if necessary. */
if( wtmp || utmp) {
strncpy( ut.ut_name, name, sizeof( ut.ut_name));
}
/* Setup the line and time. */
strncpy( ut.ut_line, pty, sizeof( ut.ut_line));
time( &( ut.ut_time));
/* Log to lastlog as needed. */
if( lastlog) {
int f=lopen( "/usr/adm/lastlog", O_WRONLY);
if( f>=0) {
struct lastlog llog;
bzero( &llog, sizeof( llog));
llog.ll_time=ut.ut_time;
strncpy( llog.ll_line, ut.ut_line, sizeof( llog.ll_line));
lseek( f, uid*sizeof( llog), L_SET);
write( f, &llog, sizeof( llog));
lclose( f);
} else {
perror( "opening /usr/adm/lastlog");
}
}
/* Log to utmp and wtmp as needed. */
if( utmp) {
writeUtmp( &ut, [self getSlot:pty]);
}
if( wtmp) {
writeWtmp( &ut);
}
}
/* If needed, set pty ownership to the new user, with permissions
* set for owner read/write, group write. Group ownership set
* to the tty group, if available.
*/
if( ownership) {
struct group *gr=getgrnam( "tty");
fixOwnership( pty, uid, gr ? gr->gr_gid : -1, 0620);
}
return 0;
}
-(int)login:(char *)pty ownerships:(int)ownership utmp:(int)utmp
{
#if 0
return [self login:pty ownerships:ownership utmp:utmp wtmp:utmp lastlog:utmp];
#else /* This may make more sense. */
return [self login:pty ownerships:ownership utmp:utmp wtmp:YES lastlog:YES];
#endif
}
-(int)logout:(char *)pty ownerships:(int)ownership
utmp:(int)utmp wtmp:(int)wtmp lastlog:(int)lastlog
{
if( utmp || wtmp) {
struct utmp ut;
/* Clean up the utmp entry. */
bzero( &ut, sizeof( ut));
strncpy( ut.ut_line, pty, sizeof( ut.ut_line));
time( &( ut.ut_time));
if( utmp) {
writeUtmp( &ut, [self getSlot:pty]);
}
if( wtmp) {
writeWtmp( &ut);
}
}
/* If needed, set pty ownership back to root user, with permissions
* set for all read/write. Group ownership reset to the tty
* group, if available.
*/
if( ownership) {
struct group *gr=getgrnam( "tty");
fixOwnership( pty, 0, gr ? gr->gr_gid : -1, 0666);
}
return 0;
}
-(int)logout:(char *)pty ownerships:(int)ownership utmp:(int)utmp
{
#if 0
return [self logout:pty ownerships:ownership utmp:utmp wtmp:utmp lastlog:utmp];
#else /* This may make more sense. */
return [self logout:pty ownerships:ownership utmp:utmp wtmp:YES lastlog:YES];
#endif
}
/* I use this routine to let Stuart _kindly_ ask slog to exit. I
* don't want Stuart doing a kill() on slog while slog's in the middle
* of something ...
*/
-(void)exit
{
exit( 0);
}
/* Disconnect our controlling tty and connect to the console device. */
- ttyDisconnect
{
int tty;
tty=open( "/dev/tty", O_RDWR);
if( tty>-1) {
ioctl( tty, TIOCNOTTY, 0);
close( tty);
}
tty=open( "/dev/console", O_WRONLY);
setpgrp( 0, getpid());
dup2( tty, 1);
dup2( tty, 2);
if( tty!=1 && tty!=2) {
close( tty);
}
return self;
}
/* Catch inadvertant parent process death. */
void notifyPortHandler( notification_t *msg, SLogger *self)
{
if( msg->notify_header.msg_id==NOTIFY_PORT_DELETED) {
if( msg->notify_port==self->parentNotify) {
[self exit];
}
}
}
- run
{
kern_return_t ret;
msg_header_t initMsg;
extern int getppid( void);
task_t parentTask;
port_t notify;
[self ttyDisconnect];
/* Set up objects for our Listening pleasure. */
listener=[[SLogListener allocFromZone:[self zone]] init];
[listener setDelegate:self];
[listener usePrivatePort];
[listener addPort];
/* Give us plenty of leeway for when people logout.
* Actually, even this isn't really that great,
* but what can you do?
*/
port_set_backlog( task_self(), [listener listenPort], PORT_BACKLOG_MAX);
/* Find our parent's notify port. */
ret=task_by_unix_pid( task_self(), getppid(), &parentTask);
if( ret!=KERN_SUCCESS) {
printf( "slog: Unable to get parent's task_t.\n");
exit( 1);
}
ret=task_get_notify_port( parentTask, &parentNotify);
initMsg.msg_remote_port=parentNotify;
if( ret!=KERN_SUCCESS) {
printf( "slog: Unable to get parent's notify port.\n");
exit( 1);
}
port_allocate( task_self(), ¬ify);
task_set_notify_port( task_self(), notify);
DPSAddPort( notify, (void *)notifyPortHandler, 64, self, 31);
/* Set up the rest of the header. */
initMsg.msg_simple=TRUE;
initMsg.msg_size=sizeof( initMsg);
initMsg.msg_type=MSG_TYPE_NORMAL;
initMsg.msg_id=0;
/* Including the port which our Listener listens on. */
initMsg.msg_local_port=[listener listenPort];
/* Send it, and if successful, enter the event loop. */
ret=msg_send( &initMsg, SEND_TIMEOUT, 30000);
if( ret==KERN_SUCCESS) {
[Listener run];
}
printf( "slog: Unable to send Listener port to parent.\n");
exit( 1);
return self;
}
@end
void main( void)
{
SLogger *logger=[[SLogger alloc] init];
[logger run];
}